package com.tiger.utils;

import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.fasterxml.jackson.databind.JsonNode;

public class Wechat {
	private final static String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";
	private final static String MSG_URL = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token={0}";
	private final static String MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token={0}";
	private final static String OPENID_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type=authorization_code";
	private final static String API_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={0}&type=wx_card";
	
	private final String appID;
	private final String appsecret;
	private final String token;
	
	public Wechat(String appID, String appsecret, String token) {
		this.appID = appID;
		this.appsecret = appsecret;
		this.token = token;
	}
	
	public String getToken() {
		String key = "weixin_token" + appID;
		String token = Redis.get(key);
		if(token != null) {
			return token;
		}
		String result = Http.get(MessageFormat.format(TOKEN_URL, appID, appsecret));
		JsonNode json = Json.toJsonNode(result);
		token = json.get("access_token").asText();
		int expires_in = json.get("expires_in").asInt();
		Redis.setex(key, token, expires_in / 2, TimeUnit.SECONDS);
		return token;
	}
	
	public String getApiTicket() {
		String key = "weixin_api_ticket" + appID;
		String apiTicket = Redis.get(key);
		if(apiTicket != null) {
			return apiTicket;
		}
		String token = getToken();
		if(StringUtils.isEmpty(token)) {
			return null;
		}
		String result = Http.get(MessageFormat.format(API_TICKET_URL, token));
		JsonNode json = Json.toJsonNode(result);
		if(json.get("errcode").asInt() != 0) {
			return null;
		}
		apiTicket = json.get("ticket").asText();
		int expires_in = json.get("expires_in").asInt();
		Redis.setex(key, apiTicket, expires_in / 2, TimeUnit.SECONDS);
		return apiTicket;
	}
	
	
	public Map<String, Object> getApiConf(String url) {
		long timestamp = System.currentTimeMillis() / 1000;
		String apiTicket = getApiTicket();
		String noncestr = IdGenerator.uuid();
		
		if(StringUtils.isEmpty(apiTicket)) {
			return null;
		}
		
		Map<String, Object> conf = new HashMap<>();
		conf.put("noncestr", noncestr);
		conf.put("jsapi_ticket", apiTicket);
		conf.put("timestamp", timestamp);
		conf.put("url", url);
		
		String signature = signature(conf);
		
		conf.put("signature", signature);
		conf.put("appId", appID);
		return conf;
	}
	
	private String signature(Map<String, Object> map) {
		StringBuilder sb = new StringBuilder();
		map = new TreeMap<>(map);
		map.forEach((k, v) -> {
			if(sb.length() > 0) {
				sb.append('&');
			}
			sb.append(k).append("=").append(v);
		});
		return SHA1.encode(sb.toString());
	}
	
	public String getOpenid(String code) {
		if(StringUtils.isEmpty(code)) {
			return null;
		}
		String result = Http.get(MessageFormat.format(OPENID_URL, appID, appsecret, code));
		JsonNode json=  Json.toJsonNode(result);
		if(json.has("errcode")) {
			return null;
		}
		return json.get("openid").asText();
	}
	
	public String updateMenu(String menu) {
		return Http.postString(MessageFormat.format(MENU_URL, getToken()), menu.toString());
	}
	

	public String valid(String echostr, String signature, String timestamp, String nonce) {
		List<String> tmpArr = new ArrayList<>();
		tmpArr.add(token);
		tmpArr.add(timestamp);
		tmpArr.add(nonce);
		tmpArr.sort(Comparator.naturalOrder());
		
		String tmpStr = tmpArr.get(0) + tmpArr.get(1) + tmpArr.get(2);
		tmpStr = SHA1.encode(tmpStr);
		if(tmpStr.equals(signature)) {
			return echostr;
		}
		return null;
	}
	
	public boolean pushNews(String to, String title, String desc, String picUrl, String url) {
		if(StringUtils.isEmpty(to)) {
			return false;
		}
		
		String result = Http.postString(MessageFormat.format(MSG_URL, getToken()), transmitNewsJson(to, title, desc, picUrl, url));
		System.out.println(result);
		if(StringUtils.isEmpty(result)) {
			return false;
		}
		JsonNode json = Json.toJsonNode(result);
		return json.get("errcode").asInt() == 0;
	}
	
	public String responseMsg(String timestamp, String nonce, String msg_signature, String encrypt_type, String postStr) {
		encrypt_type = !StringUtils.isEmpty(encrypt_type) && encrypt_type.equals("aex") ? "aes" : "raw";
		
		if(!StringUtils.isEmpty(postStr)) {
			if(encrypt_type.equals("aes")) {
//				String pc = new WXBizMsgCrypt(token, appsecret, appID);
//				System.out.println(postStr);
//				String decryptMsg = "";
//				String errCoede = decryptMsg(msg_signature, timestamp, nonce, postStr, decryptMsg);
//				postStr = decryptMsg;
			}
			Element postObj = parseBody(postStr);
			String rxType = postObj.elementText("MsgType");
			String result = null;
			switch (rxType) {
			case "event":
				result = receiveEvent(postObj);
				break;
			case "text":
				result = receiveText(postObj);
				break;
			}
			if(encrypt_type.equals("aes")) {
//				result = encryptMsg(result, timestamp, nonce);
//				System.out.println("result:" + result);
			}
			return result;
		}
		return null;
	}
	
	public static Element parseBody(String postStr) {
		try {
			return new SAXReader().read(new StringReader(postStr)).getRootElement();
		} catch (DocumentException e) {
			return null;
		}
	}
	
	private String receiveEvent(Element postObj) {
		return transmitNewsXml(postObj.elementText("ToUserName"), postObj.elementText("FromUserName"), "andy的公众号", "欢迎来到andy的公众号", "http://andy.pig84.cn/zeus/qq.png", "http://www.baidu.com");
	}

	private String receiveText(Element postObj) {
		return transmitNewsXml(postObj.elementText("ToUserName"), postObj.elementText("FromUserName"), "andy的公众号", "欢迎来到andy的公众号", "http://andy.pig84.cn/zeus/qq.png", "http://www.baidu.com");
	}
	
	private String transmitNewsJson(String to, String title, String desc, String picUrl, String url) {
        List<Map<String,String>>articles=new ArrayList<>();
		Map<String,String> article = new HashMap<>();
		article.put("title", title);
		article.put("description", desc);
		article.put("url", url);
		if(!StringUtils.isEmpty(picUrl)) {
			article.put("picurl", picUrl);
		}
		articles.add(article);
		Map<String, List<Map<String, String>>> news = new HashMap<>();
		news.put("articles", articles);
		
		Map<String,Object> json = new HashMap<>();
		json.put("touser", to);
		json.put("msgtype", "news");
		json.put("news", news);
		return Json.toJson(json);
	}
	private String transmitNewsXml(String from, String to, String title, String desc, String picUrl, String url) {
		String itemTpl = "<item>"
				+ "<Title><![CDATA[{0}]]></Title>"
				+ "<Description><![CDATA[{1}]]></Description>"
				+ "<PicUrl><![CDATA[{2}]]></PicUrl>"
				+ "<Url><![CDATA[{3}]]></Url>"
				+ "</item>";
		String itemStr = MessageFormat.format(itemTpl, title, desc, picUrl, url);
		String xmlTpl = "<xml>"
				+ "<ToUserName><![CDATA[{0}]]></ToUserName>"
				+ "<FromUserName><![CDATA[{1}]]></FromUserName>"
				+ "<CreateTime><![CDATA[{2}]]></CreateTime>"
				+ "<MsgType><![CDATA[news]]></MsgType>"
				+ "<ArticleCount>{3}</ArticleCount>"
				+ "<Articles>{4}</Articles>"
				+ "</xml>";
		return MessageFormat.format(xmlTpl, 
				to, 
				from, 
				String.valueOf(System.currentTimeMillis() / 1000), 
				1, itemStr);
	}
	
	
	public String createAuthorizeUrl(String redirectURL, String state) {
		try {
			redirectURL = URLEncoder.encode(redirectURL, "utf-8");
		} catch (UnsupportedEncodingException e) {
		}
		String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid="
				+ appID
				+ "&redirect_uri=" + redirectURL
				+ "&response_type=code&scope=snsapi_base"
				+ "&state=" + state + "#wechat_redirect";
		return url;
	}
	
	public Map<String,String> createClickMenu(String name, String key) {
		Map<String,String> json = new HashMap<>();
		json.put("type", "click");
		json.put("name", name);
		json.put("key", key);
		return json;
	}
	
	public Map<String,String> createViewMenu(String name, String url) {
		Map<String,String>json = new HashMap<String,String>();
		json.put("type", "view");
		json.put("name", name);
		json.put("url", url);
		return json;
	}
	
	@SuppressWarnings("unchecked")
	public Map<String, Object> createParentMenu(String name, Map<String, Object>... subs) {
		Map<String, Object> json = new HashMap<>();
		json.put("name", name);
		json.put("sub_button", subs);
		return json;
	}
	
	public String createMainMenu(List<Map<String, String>> subButtons) {
		Map<String, List<Map<String, String>>> json = new HashMap<String, List<Map<String, String>>>();
		json.put("button", subButtons);
		return Json.toJson(json);
	}

}
